#ifndef NE_STL_INCLUDENE_STL_ITERATOR_H
#define NE_STL_INCLUDENE_STL_ITERATOR_H

#include "ne_stl_build.h"

namespace nstd {

template <class TElement>
struct _iterator_type {
    TElement* first_;
    const std::size_t* size_;
    std::size_t pos_;
    _iterator_type(TElement* first_ = nullptr, const std::size_t* size_ = nullptr, std::size_t pos_ = 0)
        : first_(first_)
        , size_(size_)
        , pos_(pos_) {}
};
template <class TElement>
using iterator_type = nstd::_iterator_type<TElement>;

template <class TElement>
class _const_iterator {
public:
    using value_type = TElement;
    using iterator_category = std::bidirectional_iterator_tag;
    using difference_type = size_t;
    using pointer = TElement*;
    using reference = TElement&;
    _const_iterator() = default;
    _const_iterator(nstd::iterator_type<TElement> p)
        : current_(p) {}
    nstd::iterator_type<TElement>& current() { return current_; }
    const nstd::iterator_type<TElement>& current() const { return current_; }
    const TElement& operator*() const { return retrieve(); }
    const TElement& operator[](int32_t i) const { return _randomAccess(i); }
    _const_iterator& operator++() {
        _increment();
        return *this;
    }
    _const_iterator operator++(int32_t) {
        _const_iterator old = *this;
        _increment();
        return old;
    }
    _const_iterator& operator--() {
        _decrement();
        return *this;
    }
    _const_iterator operator--(int32_t) {
        _const_iterator old = *this;
        _decrement();
        return old;
    }

    bool operator==(const _const_iterator& rhs) const { return current_.first_ == rhs.current_.first_ && current_.pos_ == rhs.current_.pos_; }
    bool operator!=(const _const_iterator& rhs) const { return !(*this == rhs); }
    _const_iterator operator+(std::size_t i) {
        _const_iterator toReturn;
        _add(toReturn, i);
        return toReturn;
    }
    _const_iterator operator-(std::size_t i) {
        _const_iterator toReturn;
        _subtract(toReturn, i);
        return toReturn;
    }

protected:
    nstd::iterator_type<TElement> current_;
    TElement& retrieve() const { return *(current_.first_ + current_.pos_); }
    void _increment() {
        if (current_.pos_ > *current_.size_) {
            throw;
        }
        ++current_.pos_;
    }
    void _decrement() {
        if (current_.pos_ == 0) {
            throw;
        }
        --current_.pos_;
    }
    TElement& _randomAccess(std::size_t i) const {
        if (i < 0 || i >= *current_.size_) {
            throw;
        }
        return *(current_.first_ + i);
    }
    void _add(_const_iterator& toReturn, std::size_t i) {
        if (current_.pos_ + i > *current_.size_) {
            throw;
        }
        toReturn = *this;
        toReturn.current_.pos_ += i;
    }
    void _subtract(_const_iterator& toReturn, std::size_t i) {
        if (current_.pos_ < i) {
            throw;
        }
        toReturn = *this;
        toReturn.current_.pos_ -= i;
    }
};
template <class TElement>
using const_iterator = nstd::_const_iterator<TElement>;

template <class TElement>
class _iterator : public nstd::_const_iterator<TElement> {
    using _parent_t = nstd::_const_iterator<TElement>;

public:
    _iterator() = default;
    _iterator(nstd::iterator_type<TElement> p)
        : _parent_t(p) {}

    TElement& operator*() { return _parent_t::retrieve(); }
    const TElement& operator*() const { return _parent_t::operator*(); }
    TElement& operator[](int32_t i) { return _parent_t::_randomAccess(i); }
    const TElement& operator[](int32_t i) const { return _parent_t::operator[](i); }
    _iterator& operator++() {
        _parent_t::_increment();
        return *this;
    }
    _iterator operator++(int32_t) {
        _iterator old = *this;
        _parent_t::_increment();
        return old;
    }
    _iterator& operator--() {
        _parent_t::_decrement();
        return *this;
    }
    _iterator operator--(int32_t) {
        _iterator old = *this;
        _parent_t::_decrement();
        return old;
    }
    _iterator operator+(std::size_t i) {
        _iterator toReturn;
        _parent_t::_add(toReturn, i);
        return toReturn;
    }
    _iterator operator-(std::size_t i) {
        _iterator toReturn;
        _parent_t::_subtract(toReturn, i);
        return toReturn;
    }
    std::size_t operator-(_iterator right) { return 0; }
};
template <class TElement>
using iterator = _iterator<TElement>;

template <class TElement>
using const_pointer = nstd::const_iterator<TElement>;
template <class TElement>
using pointer = nstd::iterator<TElement>;

template <class TElement>
class _const_reverse_iterator : public nstd::_const_iterator<TElement> {
    using _parent_t = nstd::const_iterator<TElement>;

public:
    _const_reverse_iterator() = default;
    explicit _const_reverse_iterator(nstd::iterator_type<TElement> p)
        : _parent_t(p) {}

    const TElement& operator*() const { return _parent_t::operator*(); }
    const TElement& operator[](int32_t i) const { return _parent_t::operator[](i); }
    _const_reverse_iterator& operator++() {
        _increment();
        return *this;
    }
    _const_reverse_iterator operator++(int32_t) {
        _const_reverse_iterator old = *this;
        _increment();
        return old;
    }
    _const_reverse_iterator& operator--() {
        _decrement();
        return *this;
    }
    _const_reverse_iterator operator--(int32_t) {
        _const_reverse_iterator old = *this;
        _decrement();
        return old;
    }

    _const_reverse_iterator operator+(std::size_t i) {
        _const_reverse_iterator toReturn;
        _add(toReturn, i);
        return toReturn;
    }
    _const_reverse_iterator operator-(std::size_t i) {
        _const_reverse_iterator toReturn;
        _subtract(toReturn, i);
        return toReturn;
    }

protected:
    void _increment() {
        if (_parent_t::current_.pos_ == 0) {
            _parent_t::current_.pos_ = *_parent_t::current_.size_;
            return;
        } else if (_parent_t::current_.pos_ == *_parent_t::current_.size_) {
            throw;
        }
        _parent_t::_decrement();
    }
    void _decrement() {
        if (_parent_t::current_.pos_ == *_parent_t::current_.size_) {
            _parent_t::current_.pos_ = 0;
            return;
        }
        if (_parent_t::current_.pos_ + 1 == *_parent_t::current_.size_) {
            throw;
        }
        _parent_t::_increment();
    }
    void _add(_const_reverse_iterator& toReturn, std::size_t i) {
        if (_parent_t::current_.pos_ == *_parent_t::current_.size_ && !i) {
            throw;
        }
        if (this->current_.pos_ == i - 1) {
            _parent_t::current_.pos_ = *_parent_t::current_.size_;
            return;
        } else if (this->current_.pos_ < i - 1)
            throw;
        toReturn = *this;
        toReturn.current_.pos_ -= i;
    }
    void _subtract(_const_reverse_iterator& toReturn, std::size_t i) {
        if (*_parent_t::current_.size_ <= _parent_t::current_.pos_ + i)
            throw;
        toReturn = *this;
        toReturn.current_.pos_ += i;
    }
};
template <class TElement>
using const_reverse_iterator = nstd::_const_reverse_iterator<TElement>;

template <class TElement>
class _reverse_iterator : public nstd::_const_reverse_iterator<TElement> {
    using _parent_t = nstd::_const_reverse_iterator<TElement>;

public:
    _reverse_iterator() = default;
    explicit _reverse_iterator(nstd::iterator_type<TElement> p)
        : _parent_t(p) {}

    TElement& operator*() { return _parent_t::retrieve(); }
    const TElement& operator*() const { return _parent_t::operator*(); }
    TElement& operator[](int32_t i) { return _parent_t::_randomAccess(i); }
    const char& operator[](int32_t i) const { return _parent_t::operator[](i); }
    _reverse_iterator& operator++() {
        _parent_t::_increment();
        return *this;
    }
    _reverse_iterator operator++(int32_t) {
        _reverse_iterator old = *this;
        _parent_t::_increment();
        return old;
    }
    _reverse_iterator& operator--() {
        _parent_t::_decrement();
        return *this;
    }
    _reverse_iterator operator--(int32_t) {
        _reverse_iterator old = *this;
        _parent_t::_decrement();
        return old;
    }
    _reverse_iterator operator+(std::size_t i) {
        _reverse_iterator toReturn;
        _parent_t::_add(toReturn, i);
        return toReturn;
    }
    _reverse_iterator operator-(std::size_t i) {
        _reverse_iterator toReturn;
        _parent_t::_subtract(toReturn, i);
        return toReturn;
    }
    std::size_t operator-(_reverse_iterator right) { return 0; }
};
template <class TElement>
using reverse_iterator = nstd::_reverse_iterator<TElement>;

}  // namespace nstd

#endif  // NE_STL_ITERATOR_H
